package com.hyhy.hycloud.uaa.aop;

import com.hyhy.hycloud.uaa.WhoisUtils;
import com.hyhy.hycloud.uaa.model.OperationLog;
import com.hyhy.hycloud.uaa.model.Org;
import com.hyhy.hycloud.uaa.model.User;
import com.hyhy.hycloud.uaa.repository.OperationLogRepository;
import com.hyhy.hycloud.uaa.repository.OrgRepository;
import com.hyhy.hycloud.uaa.repository.UserRepository;
import com.hyhy.hycloud.uaa.security.LoginRequestDetails;
import com.hyhy.hycloud.uaa.security.PasswordType;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author WUXIN
 * @date 2019/05/20
 */
@Aspect
@Component
@Order(-5)
public class OperationLogAspect {
    @Autowired
    private OperationLogRepository operationLogRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private OrgRepository orgRepository;

    /***
     * 定义controller切入点拦截规则，拦截SystemControllerLog注解的方法
     */
    @Pointcut("@annotation(com.hyhy.hycloud.uaa.aop.OperateLog)")
    public void controllerAspect(){}

    /***
     * 拦截控制层的操作日志
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("controllerAspect()")
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        OperationLog operationLog = OperationLog.builder().build();
        Object proceed = null;
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = WhoisUtils.getRealIp(request);
        operationLog.setUserIp(ip);
        operationLog.setOperateTime(LocalDateTime.now());
        getOperationLogInfo(operationLog, joinPoint);
        Object[] params =  joinPoint.getArgs();
        String info = "";
        for (Object param : params) {
            if(param instanceof UsernamePasswordAuthenticationToken){
                UsernamePasswordAuthenticationToken u = (UsernamePasswordAuthenticationToken) param;
                info += "手机号：" + u.getPrincipal().toString();
                User user = userRepository.findByMobile(u.getPrincipal().toString()).orElse(null);
                if(user != null){
                    info += "，姓名：" + user.getName();
                    if(!StringUtils.isEmpty(user.getOrgId())){
                        Org org = orgRepository.findById(user.getOrgId()).orElse(null);
                        if(org != null){
                            info += "，机构：" + org.getOrgName();
                        }
                    }
                }
                LoginRequestDetails lrd =  (LoginRequestDetails)u.getDetails();
                if(lrd.getPasswordType().equals(PasswordType.PASSWORD)){
                    operationLog.setOperateType("密码登录");
                }else {
                    operationLog.setOperateType("验证码登录");
                }
            }
        }
        operationLog.setInfo(info);
        proceed = joinPoint.proceed();
        Object result = proceed;
        if(result != null){
            operationLog.setOperateResult("成功");
        }else {
            operationLog.setOperateResult("失败");
        }
        operationLogRepository.save(operationLog);
        return result;
    }

    /***
     * 获取controller的操作信息
     * @param point
     * @return
     */
    public void getOperationLogInfo(OperationLog operationLog, ProceedingJoinPoint point) throws  Exception{
        //获取连接点目标类名
        String targetName = point.getTarget().getClass().getName() ;
        //获取连接点签名的方法名
        String methodName = point.getSignature().getName() ;
        //获取连接点参数
        Object[] args = point.getArgs() ;
        //根据连接点类的名字获取指定类
        Class targetClass = Class.forName(targetName);
        //获取类里面的方法
        Method[] methods = targetClass.getMethods() ;
        String model = "" ;
        String subModel = "";
        String operateType = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == args.length){
                    model = method.getAnnotation(OperateLog.class).model();
                    subModel = method.getAnnotation(OperateLog.class).subModel();
                    operateType = method.getAnnotation(OperateLog.class).operateType();
                    break;
                }
            }
        }
        operationLog.setModel(model);
        operationLog.setSubModel(subModel);
        operationLog.setOperateType(operateType);
    }

}
